﻿
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics;

namespace WSU.ChekaParallel.TestTask
{
    public static class TestWaitTasks
    {
        private static Task StartTask(int numLoop)
        {
            return Task.Factory.StartNew((obj) =>
                {
                    int maxloop = (int)obj;
                    for (int index = 0; index < maxloop; ++index)
                    {
                        Console.WriteLine("<{0}> working, ......", (index + 1).ToString());
                        Thread.Sleep(1000);
                    };
                }, numLoop);
        }

        /// <summary>
        /// start a timer to monitor the maximum time to wait
        /// chekanote: specify a maximum time to wait, when it times out, that is no longer waiting
        /// but the task itself is still running
        /// </summary>
        private static void TestWaitTaskByTimer()
        {
            // --------------------------------------------- wait until completion
            int maxloop = 3;
            Console.WriteLine("************** wait <{0}>-loop task run until completion **************", maxloop);
            Task fullyRunTask = StartTask(maxloop);
            fullyRunTask.Wait();
            Console.WriteLine("Task completed with status='{0}'", fullyRunTask.Status);

            // --------------------------------------------- wait early terminated
            maxloop = 5;
            Console.WriteLine("\n************** wait <{0}>-loop task, but terminate wait early **************", maxloop);
            Task waitEarlyEndTask = StartTask(maxloop);
            bool isTaskCompleted = waitEarlyEndTask.Wait(2000);

            Debug.Assert(!isTaskCompleted, "when wait times out, task is still running");
            Debug.Assert(waitEarlyEndTask.Status == TaskStatus.Running);
            Console.WriteLine("wait terminated early with task status='{0}'", waitEarlyEndTask.Status);
            Console.WriteLine("!!! no longer wait, but task is still running !!!");

            Console.ReadLine();
        }

        /// <summary>
        /// chekanote: in below sample codes, what we canceled is that "wait"
        /// not the task. after waiting is canceled, task is still running
        /// </summary>
        private static void TestWaitTaskByCancel()
        {
            CancellationTokenSource cancelSource = new CancellationTokenSource();
            CancellationToken cancelToken = cancelSource.Token;

            // ---------------- launch task to be canceled
            Task atask = StartTask(1000);

            // ---------------- launch another task to wait user's cancel signal
            Task.Factory.StartNew(() =>
            {
                Console.WriteLine("Press any key to cancel waiting any more, ......");
                Console.ReadLine();

                cancelSource.Cancel();
            });

            // ---------------- wait to be canceled
            try
            {
                atask.Wait(cancelToken);
            }
            catch (OperationCanceledException)
            {
                // chekanote: pay attention here, what we catched is "OperationCanceledException"
                // indicating that what we expected is that "wait" is canceled
                // if we want to wait that task is canceled, then we should catch "AggregateException" instead
                Console.WriteLine("No longer waiting, but task is still running !!!\ntask's status=[{0}]",atask.Status);
            }

            Debug.Assert(atask.Status == TaskStatus.Running);
            Debug.Assert(!atask.IsCanceled);

            Console.WriteLine("Press any key to exit the main thread. ");
            Console.ReadLine();
        }

        private static void TestWaitAll_NoError()
        {
            Task[] tasks = new Task[3];
            for (int index = 0; index < tasks.Length; ++index)
            {
                int sleepTime = (index + 1) * 1000;
                tasks[index] = Task.Factory.StartNew(() =>
                    {
                        Thread.Sleep(sleepTime);
                        Console.WriteLine("Task completed after <{0}>", sleepTime);
                    });
            }

            Task.WaitAll(tasks);
            foreach (Task atask in tasks)
                Debug.Assert(atask.IsCompleted);

            Console.WriteLine("all tasks completed.");
        }

        private static void TestWaitAll_Exceptions()
        {
            Task<int> okTask = Task.Factory.StartNew(() => 88);
            Task errorTask = Task.Factory.StartNew(() => { throw new InvalidOperationException("test"); });

            try
            {
                Task.WaitAll(okTask, errorTask);
                Debug.Fail("impossible to run this line of code");
            }
            catch (AggregateException ex)
            {
                AggregateException aggregatedError = ex.Flatten();
                Debug.Assert(aggregatedError.InnerExceptions.Count == 1);
                Debug.Assert(aggregatedError.InnerException is InvalidOperationException);
                Debug.Assert(aggregatedError.InnerException.Message.Equals("test"));
            }

            Console.WriteLine("DONE !!!");
        }

        private static void TestWaitAny_NoError(int maxWaitTime)
        {
            Task[] tasks = new Task[3];
            for (int index = 0; index < tasks.Length; ++index)
            {
                int id = index + 1;
                tasks[index] = Task.Factory.StartNew((obj) =>
                {
                    int taskid = (int)obj;
                    Thread.Sleep((5 - taskid) * 1000);
                    Console.WriteLine("Task<{0}> completed", taskid.ToString());
                }, id);
            }

            int completedIndex = Task.WaitAny(tasks, maxWaitTime);
            if (completedIndex >= 0)
            {
                Console.WriteLine("wait until Task<{0}> completed", tasks[completedIndex].AsyncState);
            }
            else
                Console.WriteLine("Wait early terminated !!!");
        }

        private static void TestWaitAny_Exceptions()
        {
            Task<int>[] tasks = new Task<int>[] 
            {
                Task.Factory.StartNew(()=>
                {
                    Thread.Sleep(500);
                    return 88;
                }),
                Task.Factory.StartNew(() =>
                {
                    throw new InvalidOperationException("test");
                    return 0;
                }),
                Task.Factory.StartNew(() =>
                {
                    Thread.Sleep(1000);
                    return 99;
                })
            };

            int? result = null;
            while (!result.HasValue)
            {
                int index = Task.WaitAny(tasks);
                if (tasks[index].IsFaulted)
                {
                    try
                    {
                        tasks[index].Wait();
                    }
                    catch (AggregateException ex)
                    {
                        Console.WriteLine("error: {0}", ex.InnerException.Message);
                    }
                }
                else
                {
                    result = tasks[index].Result;
                }

                // filter out those unqualified completed tasks and wait again
                tasks = tasks.Where(t => !t.IsCompleted).ToArray();
            }// while

            Console.WriteLine("final result={0}", result);
        }

        public static void TestMain()
        {
            TestWaitTaskByTimer();
            // TestWaitTaskByCancel();
            // TestWaitAll_NoError();
            // TestWaitAll_Exceptions();
            // TestWaitAny_NoError(int.MaxValue);
            // TestWaitAny_NoError(100);
            // TestWaitAny_Exceptions();
        }
    }
}
